package uk.ac.imperial.gpaexpress.client;

import java.util.Date;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.XMLParser;

public class UserInterface extends Composite {
	private static UserInterfaceUiBinder uiBinder = GWT.create(UserInterfaceUiBinder.class);
	interface UserInterfaceUiBinder extends UiBinder<DockLayoutPanel, UserInterface> {}
	@UiField Label userEmail;
	@UiField Anchor logoutAnchor;
	@UiField ListBox modelList;
	@UiField ListBox paramList;
	@UiField TabLayoutPanel tabPanel;
	@UiField FormPanel modelTextForm;
	@UiField TextBox modelTitle;
	@UiField TextArea modelText;
	@UiField Button newButton;
	@UiField Button solveButton;
	@UiField Button shareButton;
	@UiField Button closeButton;
	@UiField Button deleteButton;
	@UiField Button saveButton;
	@UiField SimplePanel constantsPanel;
	@UiField VerticalPanel variablesPanel;
	@UiField SimplePanel canvasPanel;
	@UiField HorizontalPanel analysisHeaders;
	@UiField VerticalPanel analysesPanel;
	@UiField SimplePanel tickerPanel;
	@UiField Button addAnalysisButton;
	@UiField VerticalPanel notificationPanel;
	
	Grid constantsGrid;
	Grid analysesGrid;
	HTML tickerDiv;
	Boolean parseResponse;
	Boolean loggedIn;
	
	@UiHandler("logoutAnchor")
	void logoutClick(ClickEvent e) {
		loggedIn = false;
		logout();
	}
	
	@UiHandler("addAnalysisButton")
	void addClick(ClickEvent e) {
		final DialogBox addAnalysisDialog = new DialogBox();
		AnalysisScreen analysisScreen = new AnalysisScreen(this);
		addAnalysisDialog.setText("Create new analysis");
		addAnalysisDialog.add(analysisScreen);
		addAnalysisDialog.setAnimationEnabled(true);
		addAnalysisDialog.center();
	}
	
	@UiHandler("modelList")
	void onModelSelect(ChangeEvent event){
		// If blank entry selected then clear params list and return
		if(modelList.getSelectedIndex() == 0){
			paramList.clear();
			paramList.addItem("");
			return;
		}

		// Get all parameter sets for this model and load default
		try {
			populateParamList();

		} catch (RequestException e) {
			e.printStackTrace();
		}
	}

	@UiHandler("paramList")
	void onParamSelect(ChangeEvent event){
		// If blank params selected then do nothing and return
		if(paramList.getSelectedIndex() == 0)
			return;
		try {
			getModelXml();
			getConstants();
			getResults();
		} catch (RequestException e) {
			e.printStackTrace();
		}
	}

	@UiHandler("newButton")
	void newClick(ClickEvent e){
		UploadWidget uploadScreen = new UploadWidget(this);
		DialogBox uploadDialog = new DialogBox();
		uploadDialog.setText("GPEPA file upload.");
		uploadDialog.add(uploadScreen);
		uploadDialog.setAnimationEnabled(true);
		uploadDialog.center();
	}

	@UiHandler("solveButton")
	void solveClick(ClickEvent e){
		try {
			solveModel();
		}
		catch (RequestException e1) {
			e1.printStackTrace();
		}
	}

	@UiHandler("shareButton")
	void plotClick(ClickEvent event) {
		tickerBusy();
		SharePanel sharePanel = new SharePanel(this);
		DialogBox shareDialog = new DialogBox();
		shareDialog.add(sharePanel);
		shareDialog.setText("Share model with another user.");
		shareDialog.setAnimationEnabled(true);
		shareDialog.center();
		
	}

	@UiHandler("closeButton")
	void chartClick(ClickEvent event) {
		modelList.setSelectedIndex(0);
		paramList.setSelectedIndex(0);
		clearTabs();
		paramList.clear();
	}

	@UiHandler("deleteButton")
	void deleteClick(ClickEvent event) {
		try {
			deleteModel();
		}
		catch (RequestException e) {
			e.printStackTrace();
		}
	}

	@UiHandler("saveButton")
	void saveClick(ClickEvent event) {
		// Ask for new title, check uniqueness
		final DialogBox modelDialogBox = new DialogBox();
		modelDialogBox.setText("Please choose a title for your new model.");
		final HorizontalPanel dialogPanel = new HorizontalPanel();
		final TextBox titleBox = new TextBox();
		final Button confirmButton = new Button("Ok");

		// Confirm button
		confirmButton.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				modelTitle.setText(titleBox.getText() +  ".gpepa");
				modelDialogBox.hide();
				// Submit new model form
				parseResponse = true;
				modelTextForm.submit();
			}
		});
		final Button cancelButton = new Button("Cancel");

		// Cancel button
		cancelButton.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				modelDialogBox.hide();
			}
		});
		dialogPanel.add(titleBox);
		dialogPanel.add(confirmButton);
		dialogPanel.add(cancelButton);
		modelDialogBox.setWidget(dialogPanel);
		modelDialogBox.setAnimationEnabled(true);
		modelDialogBox.center();
		titleBox.setFocus(true);
	}

	@UiHandler("modelTextForm")
	void onUpload(SubmitEvent e) {
		
	}
	
	@UiHandler("modelTextForm")
	void onUploadComplete(SubmitCompleteEvent e) {
			String model_xml = e.getResults();
			if(model_xml.equals("")){
				Window.alert("Please select a valid gpepa file.");
			}
			else{

				// Get all parameter sets for this model and load the default
				try {
					Document dom = XMLParser.parse(model_xml);
					NodeList modelNodes = dom.getElementsByTagName("model");
					Element modelEm = (Element)modelNodes.item(0);
					
					if(parseResponse==true){
						modelList.addItem(modelEm.getAttribute("title") + " #" + modelEm.getAttribute("id"));
						modelList.setSelectedIndex(modelList.getItemCount()-1);

						populateTabs(model_xml);
						populateParamList();
					}else{
						int old_id = Integer.valueOf(modelList.getValue(modelList.getSelectedIndex()).split("#")[1]);
						modelList.removeItem(modelList.getSelectedIndex());
						modelList.addItem(modelEm.getAttribute("title") + " #" + modelEm.getAttribute("id"));
						modelList.setSelectedIndex(modelList.getItemCount()-1);
						int new_id = Integer.valueOf(modelList.getValue(modelList.getSelectedIndex()).split("#")[1]);
						copyResults(old_id, new_id);
					}

				} catch (RequestException e1) {
					e1.printStackTrace();
				}
			}
	}

	
	public UserInterface() {
		initWidget(uiBinder.createAndBindUi(this));
		modelText.setCharacterWidth(76);
		modelText.setVisibleLines(28);
		modelText.getElement().setAttribute("spellCheck", "false");
		modelTextForm.setMethod(FormPanel.METHOD_GET);
		modelTextForm.setEncoding(FormPanel.ENCODING_MULTIPART); 
		modelTextForm.setAction("/Upload");
		analysesGrid = new Grid(0,0);
	}

	/* Method takes old model_id and new model_id and copies all results from old to new, deleting the old model */
	void copyResults(int old_id, int new_id) throws RequestException{
		RequestBuilder copyRequest = new RequestBuilder(RequestBuilder.POST,"/Query");
		copyRequest.setHeader("type", "copy");
		copyRequest.setHeader("old_id", String.valueOf(old_id));
		copyRequest.setHeader("new_id", String.valueOf(new_id));
		copyRequest.sendRequest(null, new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				
			}
			public void onError(Request request, Throwable exception) {
				
			}
		});
		
	}
	
	/* populate parameter listbox with sets that belong to selected model */
	void populateParamList() throws RequestException{
		tickerBusy();

		// Get model name
		String model = modelList.getValue(modelList.getSelectedIndex());

		// Clear params from previous model
		paramList.clear();
		paramList.addItem("");
		// Call servlet to return xml representation of params
		RequestBuilder paramRequest = new RequestBuilder(RequestBuilder.GET,"/Query");
		paramRequest.setHeader("model", model);
		paramRequest.setHeader("type", "params");
		paramRequest.sendRequest(null, new RequestCallback(){
			// response is xml elements: <params title=...>
			public void onResponseReceived(Request request, Response response) {
				Document dom = XMLParser.parse(response.getText());
				NodeList paramNodes = dom.getElementsByTagName("params");
				for(int i=0; i<paramNodes.getLength(); i++){
					Element em = (Element) paramNodes.item(i);
					paramList.addItem(em.getAttribute("title") + " #" + em.getAttribute("id"));
				}
				paramList.setSelectedIndex(1);
				tickerIdle();
				try {
					getModelXml();
					getResults();
				} catch (RequestException e) {
					e.printStackTrace();
				}
			}

			public void onError(Request request, Throwable exception) {

			}

		});
	}

	/* method takes model name and loads model source from database */
	private void getModelXml() throws RequestException{
		tickerBusy();
		String modelName = modelList.getValue(modelList.getSelectedIndex());
		String paramsName = paramList.getValue(paramList.getSelectedIndex());
		RequestBuilder modelRequest = new RequestBuilder(RequestBuilder.GET,"/Query");
		modelRequest.setHeader("model", modelName);
		modelRequest.setHeader("params", paramsName);
		modelRequest.setHeader("type", "model_xml");
		modelRequest.sendRequest(null, new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				populateTabs(response.getText());
			}
			public void onError(Request request, Throwable exception) {
				modelText.setText("");
			}
		});
	}

	/* method populates tabPanel with parsed model components */
	void populateTabs(String xmlString){
		clearTabs();
		try{
			Document dom = XMLParser.parse(xmlString);

			// Get model source and add to first tab
			NodeList sourceNodes = dom.getElementsByTagName("source");
			String source = sourceNodes.item(0).getChildNodes().item(0).getNodeValue();
			modelText.setText(source);

			// Get pctmc nodes and transitions
			NodeList transitionNodes = dom.getElementsByTagName("transition");
			int no_transitions = transitionNodes.getLength();
			String[] transitions = new String[no_transitions*4];
			// String array will be repeating patterns of [group, rate, fromNode, toNode]
			for(int i=0,j=0; i<no_transitions; i++){
				Element transitionEm = (Element) transitionNodes.item(i);
				transitions[j++] = transitionEm.getAttribute("group");
				transitions[j++] = transitionEm.getAttribute("rate");
				transitions[j++] = transitionEm.getAttribute("down");
				transitions[j++] = transitionEm.getAttribute("up");
			}

			// Transform transitions array into long string
			String transitionsLong = "";
			int size = transitions.length;
			transitionsLong = transitions[0];
			for (int i=1; i<size; i++) {
				transitionsLong += "|" + transitions[i] ;
			}

			// Create div target for raphael, render, and move div to tabpanel
			canvasPanel.clear();
			HTML canvas = new HTML("<div id=\"canvas\"></div>");
			RootLayoutPanel.get().add(canvas);
			drawTransitionGraph(transitionsLong);
			canvasPanel.add(canvas);
			// Get constants
			NodeList constantNodes = dom.getElementsByTagName("constant");
			int no_constants = constantNodes.getLength();
			constantsGrid = new Grid(no_constants+1,3);
			constantsPanel.add(constantsGrid);
			constantsGrid.setHTML(0, 0, "<b>Name</b>");
			constantsGrid.setHTML(0, 1, "<b>Value</b>");
			constantsGrid.getColumnFormatter().setWidth(0, "50px");
			constantsGrid.getColumnFormatter().setWidth(1, "50px");
			constantsGrid.getColumnFormatter().setWidth(2, "250px");
			constantsGrid.getCellFormatter().setHorizontalAlignment(1, 2, HasHorizontalAlignment.ALIGN_CENTER);
			for(int i=0; i<no_constants; i++){
				Element em = (Element) constantNodes.item(i);
				constantsGrid.setHTML(i+1,0,em.getAttribute("name"));
				TextBox val = new TextBox();
				val.setWidth("50px");
				val.setText(em.getAttribute("value"));
				constantsGrid.setWidget(i+1, 1, val);
			}
			Button updateParamsButton = new Button("Save new parameters");
			constantsGrid.setWidget(1,2,updateParamsButton);

			updateParamsButton.addClickHandler(new ClickHandler(){
				public void onClick(ClickEvent event) {
					validateNewParamSet();
				}
			});

			// Get variables
			NodeList variableNodes = dom.getElementsByTagName("variable");
			if(variableNodes.item(0)==null){
				variablesPanel.add(new Label("n/a."));
			}
			for(int i=0; i<variableNodes.getLength(); i++){
				Element em = (Element) variableNodes.item(i);
				Label varLabel = new Label(em.getAttribute("var")
						+ " = " + em.getAttribute("expression"));
				variablesPanel.add(varLabel);
			}

			// Setup analyses grid
			analysisHeaders.setVisible(true);
			NodeList analysisNodes = dom.getElementsByTagName("analysis");
			NodeList experimentNodes = dom.getElementsByTagName("experiment");
			int no_analyses = analysisNodes.getLength();
			int no_experiments = experimentNodes.getLength();
			
			analysesGrid.resize(no_analyses + no_experiments, 5);
			analysesPanel.add(analysesGrid);
			analysesGrid.setCellSpacing(5);
			analysesGrid.getColumnFormatter().setWidth(0, "50px");
			analysesGrid.getColumnFormatter().setWidth(1, "70px");
			analysesGrid.getColumnFormatter().setWidth(2, "90px");
			analysesGrid.getColumnFormatter().setWidth(3, "150px");
			analysesGrid.getColumnFormatter().setWidth(4, "150px");
			// Format grid
			for(int r=0; r<analysisNodes.getLength()+experimentNodes.getLength(); r++){
				analysesGrid.getCellFormatter().setHorizontalAlignment(r, 1, HasHorizontalAlignment.ALIGN_CENTER);
				analysesGrid.getCellFormatter().setHorizontalAlignment(r, 2, HasHorizontalAlignment.ALIGN_CENTER);
				analysesGrid.getCellFormatter().setVerticalAlignment(r, 0, HasVerticalAlignment.ALIGN_TOP);
				analysesGrid.getCellFormatter().setVerticalAlignment(r, 1, HasVerticalAlignment.ALIGN_TOP);
				analysesGrid.getCellFormatter().setVerticalAlignment(r, 2, HasVerticalAlignment.ALIGN_TOP);
				analysesGrid.getCellFormatter().setVerticalAlignment(r, 3, HasVerticalAlignment.ALIGN_TOP);
			}
			
			// Add analyses
			if(no_analyses>0){
				// For each row
				for(int i=0; i<no_analyses; i++){

					Element analysisEm = (Element) analysisNodes.item(i);
					analysesGrid.setHTML(i, 0, analysisEm.getAttribute("type"));
					analysesGrid.setHTML(i, 1, analysisEm.getAttribute("step_size"));
					analysesGrid.setHTML(i, 2, analysisEm.getAttribute("stop_time"));
					analysesGrid.setHTML(i, 3, "none");
					DisclosurePanel expressionsDisclosure = new DisclosurePanel("show/hide");
					TextArea expressionsText = new TextArea();
					expressionsText.setCharacterWidth(25);
					expressionsText.setVisibleLines(5);
					expressionsDisclosure.setContent(expressionsText);
					NodeList plotNodes = analysisEm.getElementsByTagName("plot");
					for(int j=0; j<plotNodes.getLength(); j++){
						Element plotEm = (Element) plotNodes.item(j);
						expressionsText.setText(expressionsText.getText() + plotEm.getAttribute("expression") + "\n");
					}
					
					analysesGrid.setWidget(i, 4, expressionsDisclosure);
				}
			}

			// Add experiments
			if(no_experiments>0){
				for(int i=0; i<no_experiments; i++){
					int row = no_analyses + i;
					// Experiment element
					Element experimentEm = (Element) experimentNodes.item(i);
					
					// Get analysis details
					Element analysisEm = (Element) experimentEm.getElementsByTagName("exp_analysis").item(0);
					analysesGrid.setHTML(row,0,analysisEm.getAttribute("type"));
					analysesGrid.setHTML(row,1,analysisEm.getAttribute("step_size"));
					analysesGrid.setHTML(row,2,analysisEm.getAttribute("stop_time"));
					
					// Get ranges
					NodeList itrNodes = experimentEm.getElementsByTagName("iterate");
					if(itrNodes.getLength()==0){
						analysesGrid.setHTML(row, 3, "minimise");
					}else{
						VerticalPanel iteratePanel = new VerticalPanel();
						for(int j=0; j<itrNodes.getLength(); j++){
							Element itrEm = (Element) itrNodes.item(j);
							
							Label iterateLabel = new Label(
									itrEm.getAttribute("constant") + " ["
									+ itrEm.getAttribute("fromVal") + " : "
									+ itrEm.getAttribute("steps") + " : "
									+ itrEm.getAttribute("toVal") + "]");
							
							iteratePanel.add(iterateLabel);
						}
						analysesGrid.setWidget(row,3,iteratePanel);
					}
					// Get plot details
					NodeList plotNodes = analysisEm.getElementsByTagName("exp_plot");
					DisclosurePanel expressionsDisclosure = new DisclosurePanel("show/hide");
					TextArea expressionsText = new TextArea();
					expressionsText.setCharacterWidth(25);
					expressionsText.setVisibleLines(5);
					expressionsDisclosure.setContent(expressionsText);
					for(int j=0; j<plotNodes.getLength(); j++){
						Element plotEm = (Element) plotNodes.item(j);
						expressionsText.setText(expressionsText.getText() + plotEm.getAttribute("expression") + "\n");
					}
					analysesGrid.setWidget(row, 4, expressionsDisclosure);
				}
			}
			modelTitle.setText(modelList.getValue(modelList.getSelectedIndex()).split("#")[0].trim());
		} catch (Exception e) {
			e.printStackTrace();
		}

		tickerIdle();
		tabPanel.selectTab(0);
		solveButton.setEnabled(true);

	}

	/* Method validates new parameters, asks for new title, and uploads to db */
	private void validateNewParamSet(){
		final String paramStr;
		String param = "";
		int no_constants = constantsGrid.getRowCount()-1;

		// Validate params and add to string for uploading
		for(int row=1; row<no_constants+1; row++){
			final TextBox valBox = (TextBox) constantsGrid.getWidget(row, 1);
			String name = constantsGrid.getHTML(row, 0);
			String val = valBox.getText();
			try{
				Double.parseDouble(val);
			}catch(NumberFormatException e){
				Window.alert("Invalid parameter.");
				return;
			}
			param += name + "," + val + ";";
		}
		paramStr = param;

		// Ask for new title, check uniqueness
		final DialogBox paramDialogBox = new DialogBox();
		paramDialogBox.setText("Please choose a title for your new parameter set.");
		final HorizontalPanel dialogPanel = new HorizontalPanel();
		final TextBox paramTitle = new TextBox();
		final Button confirmButton = new Button("Ok");

		// Confirm button
		confirmButton.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				try {
					uploadNewParamSet(paramTitle.getText(), paramStr);
				} catch (RequestException e) {
					e.printStackTrace();
				}
				paramDialogBox.hide();
			}
		});
		final Button cancelButton = new Button("Cancel");

		// Cancel button
		cancelButton.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				paramDialogBox.hide();
			}
		});
		dialogPanel.add(paramTitle);
		dialogPanel.add(confirmButton);
		dialogPanel.add(cancelButton);
		paramDialogBox.setWidget(dialogPanel);
		paramDialogBox.setAnimationEnabled(true);
		paramDialogBox.center();

	}

	/* Method takes validated param string and uploads to db for saving */
	private void uploadNewParamSet(final String title, String paramStr) throws RequestException{
		tickerBusy();
		RequestBuilder saveParamRequest = new RequestBuilder(RequestBuilder.POST, "/Query");
		saveParamRequest.setHeader("model", modelList.getItemText(modelList.getSelectedIndex()));
		saveParamRequest.setHeader("paramsTitle", title);		
		saveParamRequest.setHeader("paramStr", paramStr);
		saveParamRequest.setHeader("paramStr", paramStr);
		saveParamRequest.setHeader("type", "paramSet");
		saveParamRequest.sendRequest(null, new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				tickerIdle();
				// Get new param id from response
				String param_id = response.getText();

				// Select new parameter set
				paramList.addItem(title + " #" + param_id);
				paramList.setSelectedIndex(paramList.getItemCount()-1);

				// Clear any results from previous param set
				clearResultsTabs();
			}
			public void onError(Request request, Throwable exception) {

			}
		});
	}

	/* Method takes new model source text and saves it in the database */
	private void saveModel(String title, String source) throws RequestException{
		tickerBusy();
		RequestBuilder saveModelRequest = new RequestBuilder(RequestBuilder.GET,"/Upload");
		saveModelRequest.setHeader("type", "model_text");
		saveModelRequest.setHeader("title", title);
		saveModelRequest.sendRequest(source, new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				tickerIdle();
			}
			public void onError(Request request, Throwable exception) {

			}
		});


	}

	/* Method gets string representing new constants from db for this paramSet */
	private void getConstants() throws RequestException{
		RequestBuilder constRequest = new RequestBuilder(RequestBuilder.GET,"/Query");
		constRequest.setHeader("params", paramList.getItemText(paramList.getSelectedIndex()));
		constRequest.setHeader("type", "constStr");
		constRequest.sendRequest(null, new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				updateConstants(response.getText());
			}
			public void onError(Request request, Throwable exception) {

			}
		});
	}

	/* Method takes string representation of new constants and updates ui */
	private void updateConstants(String constStr){
		String[] consts = constStr.split(";");
		for(int i=0; i<consts.length; i++){
			String name = consts[i].split(",")[0];
			TextBox val = new TextBox();
			val.setWidth("50px");
			val.setText(consts[i].split(",")[1]);
			constantsGrid.setHTML(i+1, 0, name);
			constantsGrid.setWidget(i+1, 1, val);
		}
	}

	/* Method calls servlet to solve model and return plots */
	private void solveModel() throws RequestException {
		// Get model and params name and check for null values
		String model = modelList.getValue(modelList.getSelectedIndex());
		String params = paramList.getValue(paramList.getSelectedIndex());
		if(model.equals("") || params.equals("")){
			Window.alert("Please select valid model");
			return;
		}
		String model_name = modelList.getValue(modelList.getSelectedIndex()).split("#")[0].trim();
		final DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat("MMM dd HH:mm:ss");
		final String dateTime = dateTimeFormat.format(new Date());
		Notification loginNote = new Notification(dateTime, "Model solving for " + model_name + " initiated.");
		notificationPanel.add(loginNote);
		tickerBusy();
		RequestBuilder solveRequest = new RequestBuilder(RequestBuilder.GET, "/Solve");
		solveRequest.setHeader("model", model);
		solveRequest.setHeader("params", params);
		solveRequest.sendRequest(null, new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				tickerIdle();
				try {
					if(loggedIn){
						getNotifications();
						getResults();
					}
				} catch (RequestException e) {
					e.printStackTrace();
				}
			}
			public void onError(Request request, Throwable exception) {

			}
		});
	}

	/* Method checks database for any notifications for current user */
	private void getNotifications() throws RequestException{
		RequestBuilder notifyRequest = new RequestBuilder(RequestBuilder.GET,"/Query");
		notifyRequest.setHeader("type","notifications");
		notifyRequest.sendRequest(null,new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				String notificationsXml = response.getText();
				Document dom = XMLParser.parse(notificationsXml);
				NodeList notifyNodes = dom.getElementsByTagName("notification");
				for(int i=0; i<notifyNodes.getLength(); i++){
					Element notifyEm = (Element) notifyNodes.item(i);
					Notification note = new Notification(notifyEm.getAttribute("timestamp"),notifyEm.getAttribute("message"));
					notificationPanel.add(note);
				}
				
			}
			public void onError(Request request, Throwable exception) {
				
			}
		});
	}
	
	/* Method calls servlet to delete param set for model, if default params then delete model */
	private void deleteModel() throws RequestException{
		// Get model and params name and check for null values
		String model = modelList.getValue(modelList.getSelectedIndex());
		String params = paramList.getValue(paramList.getSelectedIndex());
		if(model.equals("") || params.equals("")){
			Window.alert("Please select valid model");
			return;
		}
		tickerBusy();
		RequestBuilder deleteRequest = new RequestBuilder(RequestBuilder.POST, "/Query");
		deleteRequest.setHeader("type", "delete");
		deleteRequest.setHeader("model", model);
		deleteRequest.setHeader("params", params);
		deleteRequest.sendRequest(null, new RequestCallback(){
			public void onResponseReceived(Request request, Response response) {
				if(response.getText().equals("model_deleted")){
					modelList.removeItem(modelList.getSelectedIndex());
				}
				paramList.clear();
				modelList.setSelectedIndex(0);
				paramList.setSelectedIndex(0);
				clearTabs();
				tickerIdle();
			}
			public void onError(Request request, Throwable exception) {

			}
		});
	}

	/* Method gets results datasets from db, generates plots, and add graphs to Ui */
	private void getResults() throws RequestException{
		clearResultsTabs();
		tickerBusy();
		// Get model and params name and check for null values
		String model = modelList.getValue(modelList.getSelectedIndex());
		String params = paramList.getValue(paramList.getSelectedIndex());
		if(model.equals("") || params.equals("")){
			Window.alert("Please select valid model");
			return;
		}
		RequestBuilder plotRequest = new RequestBuilder(RequestBuilder.GET,"/Query");
		plotRequest.setHeader("model", model);
		plotRequest.setHeader("params", params);
		plotRequest.setHeader("type", "results");
		plotRequest.sendRequest(null, new RequestCallback(){

			public void onResponseReceived(Request request, Response response) {
				GWT.log(response.getText());
				tickerIdle();
				parseResults(response.getText());
			}

			public void onError(Request request, Throwable exception) {

			}

		});
	}

	/* method parses xml representation of results and generated plot data */
	private void parseResults(String resultsXml){
		tickerBusy();
		// Get plots nodes
		Document dom = XMLParser.parse(resultsXml);
		NodeList plotNodes = dom.getElementsByTagName("plot");
		if(plotNodes.getLength()>0){
			// For each plot node create arrays for time and counts
			for(int i=0; i<plotNodes.getLength(); i++){

				// Plot details
				String chartTarget = "chart_" + String.valueOf(i+1);
				Element plotEm = (Element) plotNodes.item(i);
				String type = plotEm.getAttribute("type");
				double stepSize = Double.valueOf(plotEm.getAttribute("stepSize"));
				double stopTime = Double.valueOf(plotEm.getAttribute("stopTime"));
				String title = type + " : stepSize=" + stepSize + ", stopTime=" + stopTime;

				// Count y-axis data
				NodeList xNodes = plotEm.getElementsByTagName("x_data");
				NodeList yNodes = plotEm.getElementsByTagName("y_data");
				NodeList zNodes = plotEm.getElementsByTagName("z_data");

				if(yNodes.getLength()==0 && zNodes.getLength()==0){
					// Optimal value
					Element valEm = (Element) xNodes.item(0);
					tabPanel.add(new Label("Optimal value of " + valEm.getAttribute("name") + " = " 
							+ valEm.getAttribute("optimum")),String.valueOf(i+1));


				}else if(xNodes.getLength()==0 && zNodes.getLength()==0){
					// Standard time-count analysis
					String countStr = "";
					for(int j=0; j<yNodes.getLength(); j++){
						Element countEm = (Element) yNodes.item(j);
						countStr += countEm.getAttribute("label") + ",";
						String s = countEm.getChildNodes().item(0).getNodeValue();
						countStr += s.substring(1, s.length()-1);
						if(j<yNodes.getLength()-1)
							countStr += ";";
					}
					// Create target for JS chart library and draw chart
					HTML chartContainer = new HTML("<div id=\"" + chartTarget + "\" style=\"width: 620px; height: 490px\"></div>");
					RootPanel.get().add(chartContainer);
					draw2dChart(chartTarget,title,"Time",0,stepSize,countStr);
					tabPanel.add(chartContainer,String.valueOf(i+1));

				}else if(zNodes.getLength()==0){
					// 1d iteration
					Element iterateEm = (Element)xNodes.item(0);
					String iterateStr = iterateEm.getChildNodes().item(0).getNodeValue();
					double x_start = Double.valueOf(iterateStr.split(",")[0]);
					double x_step = Double.valueOf(iterateStr.split(",")[1]);
					String countStr = "";
					for(int j=0; j<yNodes.getLength(); j++){
						Element countEm = (Element) yNodes.item(j);
						countStr += countEm.getAttribute("label") + ",";
						String s = countEm.getChildNodes().item(0).getNodeValue();
						countStr += s.substring(1, s.length()-1);
						if(j<yNodes.getLength()-1)
							countStr += ";";
					}
					// Create target for JS chart library and draw chart
					HTML chartContainer = new HTML("<div id=\"" + chartTarget + "\" style=\"width: 620px; height: 490px\"></div>");
					RootPanel.get().add(chartContainer);
					draw2dChart(chartTarget,title,iterateEm.getAttribute("axis"),x_start,x_step,countStr);
					tabPanel.add(chartContainer,String.valueOf(i+1));
				}else{
					// 2d iteration
					// Get x series
					Element iterateXEm = (Element)xNodes.item(0);
					String x_label = iterateXEm.getAttribute("axis");
					String iterateXStr = iterateXEm.getChildNodes().item(0).getNodeValue();
					double x_start = Double.valueOf(iterateXStr.split(",")[0]);
					double x_step = Double.valueOf(iterateXStr.split(",")[1]);

					// Get y series
					Element iterateYEm = (Element)yNodes.item(0);
					String y_label = iterateYEm.getAttribute("axis");
					String iterateYStr = iterateYEm.getChildNodes().item(0).getNodeValue();
					double y_start = Double.valueOf(iterateYStr.split(",")[0]);
					double y_step = Double.valueOf(iterateYStr.split(",")[1]);

					// Get z series
					Element zMapEm = (Element)zNodes.item(0);
					String z_label = zMapEm.getAttribute("label");
					String zMapString = zMapEm.getChildNodes().item(0).getNodeValue();
					// Get rows & cols
					String[] zCols = zMapString.split(";");
					String[] zRow = zCols[0].substring(1, zCols[0].length()-2).split(",");
					int rows = zCols.length;
					int cols = zRow.length;

					// Get max
					double max = 0;
					for(String colStr : zCols){
						String[] colValues = colStr.substring(1, colStr.length()-2).split(",");
						for(String valStr : colValues){
							Double val = Double.valueOf(valStr);
							if(val > max && !Double.isInfinite(val)){
								max = val;
							}
						}
					}

					HTML surfaceContainer = new HTML("<div id=\"" + chartTarget + "\" style=\"width: 100%; height: 100%\"></div>");
					RootPanel.get().add(surfaceContainer);
					drawSurface(chartTarget, rows, cols, x_label, x_start, x_step, y_label, y_start, y_step, zMapString, max, z_label);
					tabPanel.add(surfaceContainer,String.valueOf(i+1));

				}
			}
			tabPanel.selectTab(3);
		}
		tickerIdle();
	}


	/* method logs user out and creates a new loginscreen popup panel */
	private void logout() {
		// Clear UI
		clearTabs();
		tickerIdle();
		tickerPanel.clear();
		userEmail.setText("");
		logoutAnchor.setText("");
		modelList.clear();
		paramList.clear();
		notificationPanel.clear();

		// New login screen
		LoginScreen loginScreen = new LoginScreen(this);
		PopupPanel popupPanel = new PopupPanel();
		popupPanel.setWidget(loginScreen);
		popupPanel.setHeight("250px");
		popupPanel.setWidth("450px");
		popupPanel.setGlassEnabled(true);
		popupPanel.center();
		loginScreen.userText.setFocus(true);
	}

	/* Method resets tabPanel to initial state */
	private void clearTabs(){
		constantsPanel.clear();
		variablesPanel.clear();
		tabPanel.selectTab(0);
		modelText.setText("");
		canvasPanel.clear();
		analysesPanel.clear();
		analysisHeaders.setVisible(false);
		clearResultsTabs();
	}

	/* Method removes all results tabs from tabPanel */
	private void clearResultsTabs(){
		while(tabPanel.remove(3)){}
	}

	void tickerStart(){
		tickerDiv = new HTML("<div id=\"tickerDiv\"><div>");
		tickerDiv.setVisible(false);
		RootPanel.get().add(tickerDiv);
		createTicker();
		tickerPanel.add(tickerDiv);
	}

	void tickerBusy(){
		tickerDiv.setVisible(true);
	}

	void tickerIdle(){
		tickerDiv.setVisible(false);
	}

	/* Method calls javascript to populate canvas with model representation */
	private native void drawTransitionGraph(String s)/*-{

		// Get array from string
		var transitionsLong = s;
		var transitions = transitionsLong.split("|");

		$wnd.drawGraph(transitions);

	}-*/;

	private native void draw2dChart(String chartTarget, String title, String x_title, double x_start, double x_step, String countStr)/*-{

		$wnd.drawChart(chartTarget, title, x_title, x_start, x_step, countStr);

	}-*/;

	private native void drawSurface(String chartTarget, int rows, int cols, String x_label, double x_start, double x_step, 
			String y_label, double y_start, double y_step, String zMapString, double max, String z_label)/*-{

		var zMap = zMapString.split(";");

		$wnd.drawSurface(chartTarget,rows,cols,x_label,x_start,x_step,y_label,y_start,y_step,zMap,max,z_label);

	}-*/;

	private native void createTicker()/*-{
		$wnd.createTicker();
	}-*/;
}
